package pl.elka.simulator.core.experiment;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import pl.elka.simulator.core.Constraint;
import pl.elka.simulator.core.EnviromentConfiguration;
import pl.elka.simulator.core.Node;
import pl.elka.simulator.core.Position;

public class EffectivnessRaitingCounter {
	public float squareSize = 0.1f;
	public float squareSizeHalf = squareSize/2;
	public float squareSizeQuarter = squareSize/4;	
	
	public float countEffectiveness(EnviromentConfiguration cfg,List<? extends Node> nodes){
		float maxX = cfg.getAreaConstraint().maxX;
		float maxY = cfg.getAreaConstraint().maxY;
		float minX = cfg.getAreaConstraint().minX;
		float minY = cfg.getAreaConstraint().minY;
		
		int xLen = (int)((maxX - minX)/squareSize) + 2;
		int yLen = (int)((maxY - minY)/squareSize) + 2;
		
		float squaresValues[][] = new float[yLen][];
		for(int i=0;i<yLen;++i){
			squaresValues[i] = new float[xLen];
		}
		//System.out.println("test");
		float valueOfAvaliableSquares = 0;
		float valPart = 0;
		
		List<Constraint> constraints = new ArrayList<Constraint>(cfg.getConstraints());
		constraints.add(cfg.getAreaConstraint());
		
		for(int y = 0; y<yLen; ++y){
			for(int x = 0; x<xLen;++x){
				valPart = countSquareValue(x,y,constraints,minX,minY);
				squaresValues[y][x] = valPart;
				if(valPart>0){
					//System.out.println("["+x+"]["+y+"]="+valPart);
				}
				valueOfAvaliableSquares+=valPart;
			}
		}
		float visitedValues = countValueOfVisited(nodes,minX,minY,xLen,yLen,squaresValues);
						
		return visitedValues/valueOfAvaliableSquares;
	}
	private float countValueOfVisited(List<? extends Node> nodes, float beginX,float beginY,int xLen,int yLen,float squaresValues[][]) {
		
		Set<Visited> visited = new HashSet<Visited>();
		int cx,cy;
		int prevX = 0,prevY = 0;
		for(Node n:nodes){
			Position prevPos = null;
			for(Position pos:n.getPath().getPath()){				
				cx = (int)((pos.x - beginX)/squareSize);
				cy = (int)((pos.y - beginY)/squareSize);
				if(cx<0 || cy<0 || cx>=xLen || cy>=yLen)
					continue;
				visited.add(new Visited(cx,cy));
				if(prevPos!=null){
					if(cx!=prevX && cy!=prevY){						
						visited.add(new Visited(prevX,cy));
						visited.add(new Visited(cx,prevY));
					}
				}				
				prevPos = pos;
				prevX = cx;
				prevY = cy;
			}
		}
		
		float visitedValue = 0;
		
		for(Visited v:visited){
			float val = squaresValues[v.y][v.x];
			if(val>0){
				//System.out.println("visited (x,y) =" + v.x + ", " + v.y +", val = " + val);
			}
			visitedValue+=val;
		}
		
		return visitedValue;
	}
	private float countSquareValue(int x, int y, List<Constraint> constraints, float beginX, float beginY) {
		float cX = beginX + x*squareSize;
		float cY = beginY + y*squareSize;
		boolean cVisited = false;
		
		//down left corner
		float dlcX = cX - squareSizeHalf;
		float dlcY = cY - squareSizeHalf;
		boolean dlVisited = false;
		
		//down right corner
		float drcX = cX + squareSizeHalf;
		float drcY = cY - squareSizeHalf;
		boolean drVisited = false;
		
		//up left corner
		float ulcX = cX - squareSizeHalf;
		float ulcY = cY + squareSizeHalf;
		boolean ulVisited = false;
		
		//up right corner
		float urcX = cX + squareSizeHalf;
		float urcY = cY + squareSizeHalf;
		boolean urVisited = false;
		
		//middle down left corner
		float mdlcX = cX - squareSizeQuarter;
		float mdlcY = cY - squareSizeQuarter;
		boolean mdlVisited = false;
		
		//middle down right corner
		float mdrcX = cX + squareSizeQuarter;
		float mdrcY = cY - squareSizeQuarter;
		boolean mdrVisited = false;
		
		//middle up left corner
		float mulcX = cX - squareSizeQuarter;
		float mulcY = cY + squareSizeQuarter;
		boolean mulVisited = false;
		
		//up right corner
		float murcX = cX + squareSizeQuarter;
		float murcY = cY + squareSizeQuarter;
		boolean murVisited = false;
		
		float value = 0;
		
		for(Constraint c:constraints){
			if(!dlVisited && c.contains(dlcX,dlcY)){
				value+=1;
				dlVisited = true;
			}
			if(!drVisited && c.contains(drcX,drcY)){
				value+=1;
				drVisited = true;
			}
			if(!ulVisited && c.contains(ulcX,ulcY)){
				value+=1;
				ulVisited = true;
			}
			if(!urVisited && c.contains(urcX,urcY)){
				value+=1;
				urVisited = true;
			}
			//middle
			if(!mdlVisited && c.contains(mdlcX,mdlcY)){
				value+=1;
				mdlVisited = true;
			}
			if(!mdrVisited && c.contains(mdrcX,mdrcY)){
				value+=1;
				mdrVisited = true;
			}
			if(!mulVisited && c.contains(mulcX,mulcY)){
				value+=1;
				mulVisited = true;
			}
			if(!murVisited && c.contains(murcX,murcY)){
				value+=1;
				murVisited = true;
			}
			
			if(!cVisited && c.contains(cX,cY)){
				value+=1;
				cVisited = true;
			}
			if(value>=9){
			//kwadrat jest otoczony ze wszystkich stron, tzn nieosiagalny
				return 0;
			}
		}
		
		return value;
	}
	static class Visited{						
		public Visited(int x, int y) {		
			this.x = x;
			this.y = y;
		}
		public int x;
		public int y;
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + x;
			result = prime * result + y;
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Visited other = (Visited) obj;
			if (x != other.x)
				return false;
			if (y != other.y)
				return false;
			return true;
		}					
	};
}